#include "host_detector.h"
#include "common.h"
#include "utils.h"
#include <QDebug>
#include <QHostAddress>
#include <QRunnable>

QString get_ipv4(pcap_addr *addresses) {
    for(auto addr = addresses; addr != nullptr; addr = addr->next) {
        if(addr->addr->sa_family == AF_INET) {
            char ip_str[INET_ADDRSTRLEN] = {0};
            inet_ntop(AF_INET, &(((struct sockaddr_in *)addr->addr)->sin_addr),
                      ip_str, INET_ADDRSTRLEN);
            return ip_str;
        }
    }

    return "";
}

std::pair<QHostAddress, QHostAddress> get_ipv4_and_mask(pcap_addr *addresses) {
    for(auto addr = addresses; addr != nullptr; addr = addr->next) {
        QHostAddress address(addr->addr);
        if(address.protocol() == QAbstractSocket::IPv4Protocol) {
            return {address, QHostAddress(addr->netmask)};
        }
    }

    return {};
}

// 计算网络中的所有主机地址
void calculate_network_hosts(struct in_addr ip, struct in_addr netmask, struct in_addr *network, struct in_addr *broadcast) {
    network->s_addr = ip.s_addr & netmask.s_addr;
    broadcast->s_addr = network->s_addr | ~netmask.s_addr;
}

HostDetector::HostDetector(QObject* parent) : QThread { parent }
{
}

HostDetector::~HostDetector()
{
    if (alldevs_)
    {
        pcap_freealldevs(alldevs_);
        alldevs_ = nullptr;
    }
}

bool HostDetector::init()
{
    start();

    return true;
}

void HostDetector::uninit()
{
}

void HostDetector::init_pcap()
{
    qDebug() << "init pcap";
    emit eth_dev_found(list_all());
}

QList<EthDev> HostDetector::list_all()
{
    if (!eth_devs_.isEmpty())
    {
        return eth_devs_;
    }

    char errbuf[PCAP_ERRBUF_SIZE] { 0 };
    int  ret = pcap_findalldevs(&alldevs_, errbuf);
    if (-1 == ret)
    {
        qDebug() << "pcap find dev failed:" << errbuf;
    }

    int wifi_index = -1;
    int eth_index  = -1;
    int index = 0;
    auto p_dev = alldevs_;
    for(auto p_dev = alldevs_; p_dev != nullptr; p_dev = p_dev->next)
    {
        // qDebug() <<"name:" << p_dev->name << ", description:" << p_dev->description << ", flag:" << p_dev->flags;
        // QString ip = get_ipv4(p_dev->addresses);
        auto [address, netmask] = get_ipv4_and_mask(p_dev->addresses);
        QString mac = get_mac_by_ip(address.toString());
        
        // qDebug() << "ip:" << ip << "mac:" << get_mac_by_ip(ip);

        eth_devs_.emplaceBack(p_dev->name, p_dev->description, address, netmask, mac);

        if (PCAP_IF_CONNECTION_STATUS_CONNECTED & p_dev->flags && !(PCAP_IF_LOOPBACK & p_dev->flags)
            && PCAP_IF_UP & p_dev->flags && PCAP_IF_RUNNING & p_dev->flags && PCAP_IF_WIRELESS & p_dev->flags)
        {
            wifi_index = index;
        }
        else if (eth_devs_.back().desc.contains("Intel(R) Ethernet"))
        {  // 无法使用flag，暂时只能判断这个不太准确
            eth_index = index;
        }
        ++index;
    }

    // 把wifi和以太网卡放到队列开头
    if (eth_index > -1)
    {
        auto item = eth_devs_.takeAt(eth_index);
        eth_devs_.push_front(item);
    }

    if (wifi_index > -1)
    {
        if (wifi_index < eth_index)
        {
            auto item = eth_devs_.takeAt(wifi_index + 1);
            eth_devs_.push_front(std::move(item));
        }
        else
        {
            auto item = eth_devs_.takeAt(wifi_index);
            eth_devs_.push_front(std::move(item));
        }
    }

    return eth_devs_;
}

void HostDetector::listen_dev(int dev_index) {
    QString dev_name = eth_devs_.at(dev_index).name;
    if (arp_listeners_.contains(dev_name))
    {
        qDebug() << dev_name << "already be listened";
        return;
    }

    char errbuf[PCAP_ERRBUF_SIZE] { 0 };

    // TODO: close handle
    pcap_t* handle = pcap_open_live(dev_name.toStdString().c_str(), 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, errbuf);
    if (!handle)
    {
        qDebug() << "Couldn't open device:" << errbuf;
        return;
    }

    auto listener = std::make_shared<ArpListener>(
            handle,
            dev_name,
            [](std::shared_ptr<arp_packet> d)
            {
            });
    listener->start();
    arp_listeners_[dev_name] = listener;

    auto arp_sender = std::make_shared<ArpRequstSender>(handle, std::make_shared<EthDev>(eth_devs_.at(dev_index)));
    arp_senders_[dev_name] = arp_sender;
}

void HostDetector::run()
{
    init_pcap();
}
